#include "widget_arg.h"
#include "widget.h"
#include "widget_resource.h"

//=====================================================================
//            Common functions to manipulate resources  
//=====================================================================

static ResourceType find_resource_type (const char* repr)
{
  static const char* type_repr [] = 
  {
    "text",
    "color",
    "title",
    "dim"
  };

  static const ResourceType type [] =
  {
    eText,
    eColor,
    eTitle,
    eDim
  };

  unsigned num;

  for (num = 0; num < sizeof (type_repr) / sizeof (type_repr [0]); ++num)
    if (!strcmp (repr, type_repr [num]))
      return type [num];

  return eNone;
}

static int init_arg (PtArg_t** arg, unsigned* num, PyObject* list, 
    int (*arg_parser)(PtArg_t* arg, PyObject* tuple))
{
  unsigned length = 0;
  PyObject *tuple;
  unsigned ind = 0;

  *arg = NULL;
  *num = 0;

  if (PyList_Check (list))
  {
    length = PyList_Size (list);

    if (length && length < MAX_ARG_NUM)
    {
      *num = length;
      *arg = (PtArg_t*) malloc (length * sizeof (PtArg_t));

#if DEBUG_LEVEL == HIGH
      REPORT ("%d", *num);
      REPORT ("%x", arg);
#endif

      for (ind = 0; ind < length; ++ind)
      {
        tuple = PyList_GetItem (list, ind);

        if (PyTuple_Check (tuple))
        {
          if (PyTuple_Size (tuple) == 2)
          {
            Py_INCREF (tuple);

            if (arg_parser (*arg + ind, tuple) == -1)
            {
              Py_DECREF (tuple);
#if 0
              PyErr_SetString (PyExc_TypeError, 
                  "Resource cannot be set");
#endif

              return -1;
            }

            Py_DECREF (tuple);
          }
          else
          {
            PyErr_SetString (PyExc_TypeError, 
                "Tuples must be of size 2");
            return -1;
          }
        }
        else
        {
          PyErr_SetString (PyExc_TypeError, 
              "Arglist must contain tuples");
          return -1;
        }
      }
    } 
    else
    {
      PyErr_SetString (PyExc_TypeError, 
          "Arglist can't be empty");
      return -1;
    }
  }

  return 0;
}

//=====================================================================
//              Class to set/get Photon resources
//=====================================================================
static PyMemberDef  WidgetArg_members [] =
{
  {NULL}  // Sentinel 
};

static PyMethodDef  WidgetArg_methods [] =
{
  {NULL}  // Sentinel 
};

static PyTypeObject WidgetArgType = 
{
  PyObject_HEAD_INIT(NULL)
  0,                         // ob_size 
  "ph.Widget_arg",            // tp_name
  sizeof(WidgetArg),         // tp_basicsize 
  0,                         // tp_itemsize
  (destructor)WidgetArg_dealloc,// tp_dealloc
  0,                         // tp_print 
  0,                         // tp_getattr 
  0,                         // tp_setattr
  0,                         // tp_compare 
  0,                         // tp_repr
  0,                         // tp_as_number
  0,                         // tp_as_sequence 
  0,                         // tp_as_mapping 
  0,                         // tp_hash 
  0,                         // tp_call
  0,                         // tp_str
  0,                         // tp_getattro
  0,                         // tp_setattro
  0,                         // tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags 
  "Photon wrapper objects",  // tp_doc 
  0,                         // tp_traverse 
  0,                         // tp_clear 
  0,                         // tp_richcompare 
  0,                         // tp_weaklistoffset 
  0,                         // tp_iter 
  0,                         // tp_iternext 
  WidgetArg_methods,         // tp_methods 
  WidgetArg_members,         // tp_members
  0,                         // tp_getset 
  0,                         // tp_base 
  0,                         // tp_dict
  0,                         // tp_descr_get 
  0,                         // tp_descr_set 
  0,                         // tp_dictoffset 
  (initproc)WidgetArg_init,  // tp_init
  0,                         // tp_alloc 
  WidgetArg_new              // tp_new 
};

static void WidgetArg_dealloc (WidgetArg* self)
{
  if (self->arg)
  {

#if DEBUG_LEVEL == HIGH
   MARK ("free (self->arg) called");
#endif

    free (self->arg);
  }

  self->ob_type->tp_free ((PyObject*) self);
}

static PyObject* WidgetArg_new (PyTypeObject* type, PyObject* args)
{
  Widget* self;

  self = (Widget*) type->tp_alloc (type, 0);
  return (PyObject*) self; 
}

static int access_none_arg (PtArg_t* arg, PyObject* value)
{
  return -1;
}

static int get_text_arg (PtArg_t* arg, PyObject* value)
{
#if DEBUG_LEVEL == HIGH
    MARK ("Getting text resource");
#endif
  WidgetResource* resource = value;
  Py_INCREF (resource);

  if (resource)
  {
    resource->type = eText;

#if DEBUG_LEVEL == HIGH
    REPORT ("%x", &resource->resource);
    REPORT ("%d", resource->type);
#endif

    PtSetArg (arg, Pt_ARG_TEXT_STRING, &resource->resource, 0);
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, 
        "Second element of tuple must be WidgetResource for"
        "getting text resource");

    return -1;
  }
}

static int set_concrete_text_arg (PtArg_t* arg, PyObject* value, long type)
{
  const char *temp;

  if (PyString_Check (value))
  {
    temp = PyString_AsString (value);

#if DEBUG_LEVEL == HIGH
    MARK ("Setting text resource");
    REPORT ("%s", temp);
    REPORT ("%x", arg);
#endif

    PtSetArg (arg, type, temp, 0);

    return 0;
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, 
        "Second element of tuple must be string for "
        "text resource");

    return -1;
  }
}

static int set_text_arg (PtArg_t* arg, PyObject* value)
{
  return set_concrete_text_arg (arg, value, Pt_ARG_TEXT_STRING);
}

static long find_color (const char *repr)
{
  if (!(strcmp (repr, "blue")))
    return Pg_BLUE;

  return Pg_BLUE;
}

static int set_color_arg (PtArg_t* arg, PyObject* value)
{
  const char *temp;

  if (PyString_Check (value))
  {
    temp = PyString_AsString (value);

#if DEBUG_LEVEL == HIGH
    MARK ("Setting color resource");
    REPORT ("%s", temp);
    REPORT ("%x", arg);
#endif

    PtSetArg (arg, Pt_ARG_COLOR, find_color (temp), 0);

    return 0;
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, 
        "Second element of tuple must be string for"
        "color resource");

    return -1;
  }
}

static int set_title_arg (PtArg_t* arg, PyObject* value)
{
  return set_concrete_text_arg (arg, value, Pt_ARG_WINDOW_TITLE);
}

static int set_dim_arg (PtArg_t* arg, PyObject* value)
{
  static PhDim_t dim = {0};

#if DEBUG_LEVEL == HIGH
  MARK ("Setting dimension arg");
#endif

  if (PyTuple_Check (value))
  {
    if (PyTuple_Size (value) == 2)
    {
      dim.w = (unsigned short) PyInt_AsLong (PyTuple_GetItem (
            value, 0));
      dim.h = (unsigned short) PyInt_AsLong (PyTuple_GetItem (
            value, 1));

#if DEBUG_LEVEL == HIGH
      REPORT ("%d", dim.w);
      REPORT ("%d", dim.h);
#endif

      PtSetArg (arg, Pt_ARG_DIM, &dim, 0);
    }
    else
    {
      PyErr_SetString (PyExc_TypeError, 
          "Dimension tuple must be of size 2");
      return -1;
    }
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, 
        "Tuple must be passed as argument of dimension");
    return -1;
  }
}

static int (*g_set_arg_table []) (PtArg_t*, PyObject*) = 
{
  access_none_arg,  // eNone,
  set_text_arg,     // eText,
  set_color_arg,    // eColor
  set_title_arg,    // eTitle
  set_dim_arg       // eDim
};

static int (*g_get_arg_table []) (PtArg_t*, PyObject*) = 
{
  access_none_arg,  // eNone,
  get_text_arg,     // eText,
  access_none_arg,  // eColor
  access_none_arg,  // eTitle
  access_none_arg,  // eDim
};

static int access_arg (PtArg_t* arg, PyObject* tuple,
    int (**concrete_arg_table) (PtArg_t* arg, PyObject* tuple))
{
  PyObject     *resource, 
               *value;
  const char   *resource_repr;
  ResourceType resource_type;

  resource = PyTuple_GetItem (tuple, 0);
  value = PyTuple_GetItem (tuple, 1);

  if (PyString_Check (resource))
  {
    resource_repr = PyString_AsString (resource);

#if DEBUG_LEVEL == HIGH
    REPORT ("%s", resource_repr);
#endif

    resource_type = find_resource_type (resource_repr);

#if DEBUG_LEVEL == HIGH
    REPORT ("%d", resource_type);
    REPORT ("%x", concrete_arg_table [resource_type]);
#endif

    return concrete_arg_table [resource_type] (arg, value);
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, 
        "First element of tuple must be string (key)");
    return -1;
  }

  return 0;
}

static int get_arg (PtArg_t* arg, PyObject* tuple)
{
  return access_arg (arg, tuple, g_get_arg_table);
}

static int set_arg (PtArg_t* arg, PyObject* tuple)
{
  return access_arg (arg, tuple, g_set_arg_table);
}

static int WidgetArg_init (WidgetArg* self, PyObject* args)
{
  const char *temp;
  PyObject *list;

  if (PyArg_ParseTuple (args, "sO", &temp, &list))
  {

#if DEBUG_LEVEL == HIGH
    REPORT ("%x", list);
    REPORT ("%s", temp);
#endif

    if (!strcmp (temp, "set"))
      return init_arg (&self->arg, &self->num, list, set_arg);

    if (!strcmp (temp, "get"))
      return init_arg (&self->arg, &self->num, list, get_arg);

    PyErr_SetString (PyExc_TypeError, 
        "argument \"get\"/\"set\" must be passed (no other keyword)");
    return NULL;
  }
  else
  {
    PyErr_SetString (PyExc_TypeError, 
        "argument \"get\"/\"set\" must be passed and then list");
    return NULL;
  }
}

//=====================================================================
//                Module declaration functions
//=====================================================================

static PyMethodDef module_methods [] = 
{
  {NULL, NULL, 0, NULL}
};

INIT_FUNC (ph_api_widget_arg, WidgetArg)
